const std = @import("std");
const os = @import("os");
const sys = @import("lib/syscall.zig");
const c_str = sys.c_str;
const fprint = @import("lib/user.zig").fprint;

export fn main(argc: usize, argv: [*]const c_str) noreturn {
    if (argc < 2) {
        ls(".", true);
    } else for (argv[1..argc]) |path| {
        ls(path, true);
    }

    sys.exit(0);
}

fn ls(path: c_str, root: bool) void {
    const fd = sys.open(path, .{ .access = .RDONLY, .no_follow = true });
    if (fd < 0) return;
    defer _ = sys.close(@intCast(fd));

    const stat: sys.Stat = st: {
        var st: sys.Stat = undefined;
        if (sys.fstat(@intCast(fd), &st) < 0) {
            fprint(2, "ls: cannot stat {s}\n", .{path});
            return;
        }
        break :st st;
    };

    if (root and stat.type == .dir) {
        var buffer: [os.fs.MAXPATH]u8 = undefined;
        var de: sys.Dirent = undefined;
        const len = @sizeOf(sys.Dirent);

        while (sys.read(@intCast(fd), std.mem.asBytes(&de), len) == len) {
            if (de.inum == 0) continue; // unallocated entries
            const sub_path = std.fmt.bufPrintZ(&buffer, "{s}/{s}", .{
                path, de.name.get(),
            }) catch @panic("PathTooLong");
            ls(sub_path, false);
        }

        return;
    }

    fprint(1, "{d: >4} {d: >8} {s}", .{ stat.ino, stat.size, std.fs.path.basename(std.mem.span(path)) });

    const suffix = switch (stat.type) {
        .dir => "/",
        .symlink => "@",
        .device => "%",
        .file, .none, _ => "",
    };
    fprint(1, "{s}\n", .{suffix});
}
